// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package ld

import (
	"bytes"
	"cmd/internal/objabi"
	"cmd/link/internal/loader"
	"cmd/link/internal/sym"
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"math/bits"
	"path/filepath"
	"sort"
	"strings"
	"sync"
)

// 此文件处理与XCOFF文件生成相关的所有算法。
// 大多数是对cmd/link/internal/pe中的内容的改编。转到
// 因为PE和XCOFF基于COFF文件。
// XCOFF生成的文件是64位的。

const (
	// 文件开头要保留的总空间
	// 用于文件头、辅助头和节头。可能会浪费一些。
	XCOFFHDRRESERVE = FILHSZ_64 + AOUTHSZ_EXEC64 + SCNHSZ_64*23

	// 基于dump-o，然后从32B四舍五入到64B，以
	// 匹配ppc64上最坏情况下的elf文本节对齐方式。
	XCOFFSECTALIGN int64 = 64

	// XCOFF二进制文件的所有部分通常应该是独立的。
	// 然而，这对于我们来说还不可能。由于某些R\u ADDR重新定位而导致的文本
	// RODATA符号内部。
	// /。数据和数据。bss是位置独立的，所以在执行过程中，它们的地址从一个无法访问的
	// 段开始，如果出现问题，则强制执行SEGFULT。
	XCOFFTEXTBASE = 0x100000000 // 文本地址的开始
	XCOFFDATABASE = 0x200000000 // 数据地址的开始
)

// 文件头
type XcoffFileHdr64 struct {
	Fmagic   uint16 // 目标机器
	Fnscns   uint16 // 节数
	Ftimedat int32  // 文件创建的时间和日期
	Fsymptr  uint64 // 到符号表的字节偏移量
	Fopthdr  uint16 // 可选头中的字节数
	Fflags   uint16 // 标志
	Fnsyms   int32  // 符号表
}

const (
	U64_TOCMAGIC = 0767 // AIX 64位XCOFF 
)

// 描述对象文件类型的标志中的条目数。
const (
	F_RELFLG    = 0x0001
	F_EXEC      = 0x0002
	F_LNNO      = 0x0004
	F_FDPR_PROF = 0x0010
	F_FDPR_OPTI = 0x0020
	F_DSA       = 0x0040
	F_VARPG     = 0x0100
	F_DYNLOAD   = 0x1000
	F_SHROBJ    = 0x2000
	F_LOADONLY  = 0x4000
)

// 辅助头
type XcoffAoutHdr64 struct {
	Omagic      int16    // 如果Vstamp为1则忽略标志
	Ovstamp     int16    // 版本
	Odebugger   uint32   // 为调试器保留
	Otextstart  uint64   // 文本的虚拟地址
	Odatastart  uint64   // 数据的虚拟地址
	Otoc        uint64   // Toc地址
	Osnentry    int16    // 入口点的节号
	Osntext     int16    // 文本的节号
	Osndata     int16    // 数据段号
	Osntoc      int16    // Toc段号
	Osnloader   int16    // 加载程序段号
	Osnbss      int16    // Bss段号
	Oalgntext   int16    // 最大文本对齐
	Oalgndata   int16    // 最大数据对齐
	Omodtype    [2]byte  // 模块类型字段
	Ocpuflag    uint8    // 位标志-对象的CPU类型
	Ocputype    uint8    // 保留给CPU类型
	Otextpsize  uint8    // /请求的文本页大小
	Odatapsize  uint8    // 请求的数据页大小
	Ostackpsize uint8    // 请求的堆栈页大小
	Oflags      uint8    // 标志和TLS对齐
	Otsize      uint64   // 文本大小字节
	Odsize      uint64   // 数据大小字节
	Obsize      uint64   // Bss大小字节
	Oentry      uint64   // 入口点地址
	Omaxstack   uint64   // 允许的最大堆栈大小
	Omaxdata    uint64   // 最大值允许的数据大小
	Osntdata    int16    // 数据段的段号
	Osntbss     int16    // Tbss段的段号
	Ox64flags   uint16   // 64位对象的附加标志
	Oresv3a     int16    // 保留
	Oresv3      [2]int32 // 保留
}

// 段头
type XcoffScnHdr64 struct {
	Sname    [8]byte // 段名
	Spaddr   uint64  // 物理地址
	Svaddr   uint64  // 虚拟地址
	Ssize    uint64  // 节大小
	Sscnptr  uint64  // 文件到原始数据的偏移量
	Srelptr  uint64  // 文件到重新定位
	Slnnoptr uint64  // 文件到行号的偏移量
	Snreloc  uint32  // 重新定位条目的数量
	Snlnno   uint32  // 行号条目
	Sflags   uint32  // 标志
}

// 定义节类型的标志。
const (
	STYP_DWARF  = 0x0010
	STYP_TEXT   = 0x0020
	STYP_DATA   = 0x0040
	STYP_BSS    = 0x0080
	STYP_EXCEPT = 0x0100
	STYP_INFO   = 0x0200
	STYP_TDATA  = 0x0400
	STYP_TBSS   = 0x0800
	STYP_LOADER = 0x1000
	STYP_DEBUG  = 0x2000
	STYP_TYPCHK = 0x4000
	STYP_OVRFLO = 0x8000
)
const (
	SSUBTYP_DWINFO  = 0x10000 // 矮人信息部分
	SSUBTYP_DWLINE  = 0x20000 // 矮人行号部分
	SSUBTYP_DWPBNMS = 0x30000 // 矮人公共名称部分
	SSUBTYP_DWPBTYP = 0x40000 // 矮人公共类型部分
	SSUBTYP_DWARNGE = 0x50000 // 矮人aranges部分
	SSUBTYP_DWABREV = 0x60000 // 矮人缩写部分
	SSUBTYP_DWSTR   = 0x70000 // 矮人字符串部分
	SSUBTYP_DWRNGES = 0x80000 // 矮人范围部分
	SSUBTYP_DWLOC   = 0x90000 // 矮人位置列表部分
	SSUBTYP_DWFRAME = 0xA0000 // 矮帧部分
	SSUBTYP_DWMAC   = 0xB0000 // 矮宏部分
)

// 头大小
const (
	FILHSZ_32      = 20
	FILHSZ_64      = 24
	AOUTHSZ_EXEC32 = 72
	AOUTHSZ_EXEC64 = 120
	SCNHSZ_32      = 40
	SCNHSZ_64      = 72
	LDHDRSZ_32     = 32
	LDHDRSZ_64     = 56
	LDSYMSZ_64     = 24
	RELSZ_64       = 14
)

// 表示所有XCOFF符号的类型。
type xcoffSym interface {
}

// 符号表条目
type XcoffSymEnt64 struct {
	Nvalue  uint64 // 符号值
	Noffset uint32 // 字符串表或中名称的偏移量。调试段
	Nscnum  int16  // 符号段号
	Ntype   uint16 // 基本和派生类型规范
	Nsclass uint8  // 符号存储类
	Nnumaux int8   // 辅助项数
}

const SYMESZ = 18

const (
	// Nscnum 
	N_DEBUG = -2
	N_ABS   = -1
	N_UNDEF = 0

	// Ntype 
	SYM_V_INTERNAL  = 0x1000
	SYM_V_HIDDEN    = 0x2000
	SYM_V_PROTECTED = 0x3000
	SYM_V_EXPORTED  = 0x4000
	SYM_TYPE_FUNC   = 0x0020 // 是函数
)

// 存储类。
const (
	C_NULL    = 0   // 标记为删除的符号表项
	C_EXT     = 2   // 外部符号
	C_STAT    = 3   // 静态符号
	C_BLOCK   = 100 // 内部块的开始或结束
	C_FCN     = 101 // 函数的开头或结尾
	C_FILE    = 103 // 源文件名和编译器信息
	C_HIDEXT  = 107 // 未命名的外部符号
	C_BINCL   = 108 // 包含文件的开头
	C_EINCL   = 109 // 包含文件的结尾
	C_WEAKEXT = 111 // 弱外部符号
	C_DWARF   = 112 // 矮子符号
	C_GSYM    = 128 // 全局变量
	C_LSYM    = 129 // 自动变量在堆栈上分配
	C_PSYM    = 130 // 在堆栈上分配给子程序的参数
	C_RSYM    = 131 // 寄存器变量
	C_RPSYM   = 132 // 在寄存器
	C_STSYM   = 133 // 静态分配的符号
	C_BCOMM   = 135 // 公共块的开始
	C_ECOML   = 136 // 公共块的本地成员
	C_ECOMM   = 137 // 公共块的结束
	C_DECL    = 140 // 对象声明
	C_ENTRY   = 141 // 备用项
	C_FUN     = 142 // 函数或过程
	C_BSTAT   = 143 // 静态块开始
	C_ESTAT   = 144 // 静态块结束
	C_GTLS    = 145 // 全局线程局部变量
	C_STTLS   = 146 // 静态线程局部变量
)

// 文件辅助项
type XcoffAuxFile64 struct {
	Xzeroes  uint32 // 名称始终在字符串表
	Xoffset  uint32 // 字符串表中的偏移量
	X_pad1   [6]byte
	Xftype   uint8 // 源文件字符串类型
	X_pad2   [2]byte
	Xauxtype uint8 // 辅助项类型
}

// 函数辅助项
type XcoffAuxFcn64 struct {
	Xlnnoptr uint64 // 指向行号的文件指针
	Xfsize   uint32 // 以字节为单位的函数大小
	Xendndx  uint32 // 下一项的符号表索引
	Xpad     uint8  // 未使用的
	Xauxtype uint8  // 辅助分录类型
}

// csect辅助分录。
type XcoffAuxCSect64 struct {
	Xscnlenlo uint32 // 长度或符号表索引的下4字节
	Xparmhash uint32 // 参数类型检查字符串的偏移量
	Xsnhash   uint16 // 。typchk段号
	Xsmtyp    uint8  // 符号对齐和类型
	Xsmclas   uint8  // 存储映射类
	Xscnlenhi uint32 // 长度或符号表索引的上4字节
	Xpad      uint8  // 未使用的
	Xauxtype  uint8  // 辅助项的类型
}

// 矮化辅助项
type XcoffAuxDWARF64 struct {
	Xscnlen  uint64 // 此符号段的长度
	X_pad    [9]byte
	Xauxtype uint8 // 辅助项的类型
}

// 辅助类型
const (
	_AUX_EXCEPT = 255
	_AUX_FCN    = 254
	_AUX_SYM    = 253
	_AUX_FILE   = 252
	_AUX_CSECT  = 251
	_AUX_SECT   = 250
)

// Xftype字段
const (
	XFT_FN = 0   // 源文件名
	XFT_CT = 1   // 编译时间戳
	XFT_CV = 2   // 编译器版本号
	XFT_CD = 128 // 编译器定义信息/

)

// 符号类型字段。
const (
	XTY_ER  = 0    // 外部引用
	XTY_SD  = 1    // 节定义
	XTY_LD  = 2    // 标签定义
	XTY_CM  = 3    // 通用csect定义
	XTY_WK  = 0x8  // 弱符号
	XTY_EXP = 0x10 // 导出符号
	XTY_ENT = 0x20 // 入口点符号
	XTY_IMP = 0x40 // 导入符号
)

// 存储映射类。
const (
	XMC_PR     = 0  // 程序代码
	XMC_RO     = 1  // 只读常量
	XMC_DB     = 2  // 调试字典表
	XMC_TC     = 3  // TOC条目
	XMC_UA     = 4  // 未分类
	XMC_RW     = 5  // 读/写数据
	XMC_GL     = 6  // 全局链接
	XMC_XO     = 7  // 扩展操作
	XMC_SV     = 8  // 32位主管调用描述符
	XMC_BS     = 9  // 310
	XMC_UC     = 11 // 未命名FORTRAN common 
	XMC_TC0    = 15 // TOC anchor 
	XMC_TD     = 16 // TOC中的标量数据项
	XMC_SV64   = 17 // 64位主管调用描述符
	XMC_SV3264 = 18 // 32位和64位
	XMC_TL     = 20 // 读/写线程本地数据
	XMC_TE     = 22 // TOC条目
)

// 加载头
type XcoffLdHdr64 struct {
	Lversion int32  // 加载段版本号
	Lnsyms   int32  // 符号表条目数
	Lnreloc  int32  // 重定位表条目数
	Listlen  uint32 // 导入文件ID字符串表长度
	Lnimpid  int32  // 导入文件ID数
	Lstlen   uint32 // 字符串表长度
	Limpoff  uint64 // 导入文件ID的开始偏移量
	Lstoff   uint64 // 字符串表的开始偏移量
	Lsymoff  uint64 // 符号表的开始偏移量
	Lrldoff  uint64 // 重新定位项的开始偏移量
}

// 加载程序符号
type XcoffLdSym64 struct {
	Lvalue  uint64 // 地址字段
	Loffset uint32 // 字节偏移量到符号名
	Lscnum  int16  // 段号的字符串表中包含符号
	Lsmtype int8   // 符号类型、导出、导入标志
	Lsmclas int8   // 符号存储类
	Lifile  int32  // 导入文件ID；导入文件ID的顺序
	Lparm   uint32 // 参数类型检查字段
}

type xcoffLoaderSymbol struct {
	sym    loader.Sym
	smtype int8
	smclas int8
}

type XcoffLdImportFile64 struct {
	Limpidpath string
	Limpidbase string
	Limpidmem  string
}

type XcoffLdRel64 struct {
	Lvaddr  uint64 // 地址字段
	Lrtype  uint16 // 重新定位大小和类型
	Lrsecnm int16  // 正在重新定位的节号
	Lsymndx int32  // 加载程序节符号表索引
}

// XCDOOLDERRELOC保存有关加载程序重新定位的信息。
type xcoffLoaderReloc struct {
	sym    loader.Sym
	roff   int32
	rtype  uint16
	symndx int32
}

const (
	XCOFF_R_POS = 0x00 // A（sym）正迁移
	XCOFF_R_NEG = 0x01 // /-A（sym）负迁移
	XCOFF_R_REL = 0x02 // A（sym-*）相对于自身
	XCOFF_R_TOC = 0x03 // A（sym TOC）相对于TOC 
	XCOFF_R_TRL = 0x12 // A（sym TOC）TOC相对间接负荷。

	XCOFF_R_TRLA = 0x13 // A（sym TOC）TOC相对加载地址。可修改指令
	XCOFF_R_GL   = 0x05 // A（sym的外部TOC）全局链接
	XCOFF_R_TCL  = 0x06 // A（sym的本地TOC）本地对象TOC地址
	XCOFF_R_RL   = 0x0C // A（sym）Pos间接加载。可修改指令
	XCOFF_R_RLA  = 0x0D // A（sym）Pos加载地址。可修改指令
	XCOFF_R_REF  = 0x0F // AL0（sym）非重定位引用无垃圾收集
	XCOFF_R_BA   = 0x08 // A（sym）绝对分支。无法修改指令
	XCOFF_R_RBA  = 0x18 // A（sym）绝对分支。可修改指令
	XCOFF_R_BR   = 0x0A // A（sym-*）分支与自身相关。不可修改的
	XCOFF_R_RBR  = 0x1A // A（sym-*）分支与自身相关。可修改的指令

	XCOFF_R_TLS    = 0x20 // 对TLS符号
	XCOFF_R_TLS_IE = 0x21 // 对TLS符号
	XCOFF_R_TLS_LD = 0x22 // 对TLS符号
	XCOFF_R_TLS_LE = 0x23 // 对TLS符号
	XCOFF_R_TLS_LE = 0x23 // 对TLS符号
	XCOFF_R_TLSM   = 0x24 // 对TLS符号
	XCOFF_R_TLSML  = 0x25 // 对本地（自身）模块

	XCOFF_R_TOCL = 0x31 // 相对于TOC——低阶位
)

type XcoffLdStr64 struct {
	size uint16
	name string
}

// XCOFF文件用于构建XCOFF文件。
type xcoffFile struct {
	xfhdr           XcoffFileHdr64
	xahdr           XcoffAoutHdr64
	sections        []*XcoffScnHdr64
	sectText        *XcoffScnHdr64
	sectData        *XcoffScnHdr64
	sectBss         *XcoffScnHdr64
	stringTable     xcoffStringTable
	sectNameToScnum map[string]int16
	loaderSize      uint64
	symtabOffset    int64                // 符号表开始的偏移量
	symbolCount     uint32               // 写入的符号表记录数
	symtabSym       []xcoffSym           // XCOFF符号表
	dynLibraries    map[string]int       // 中的动态库。装载机部分。整数表示其导入文件号（-1）
	loaderSymbols   []*xcoffLoaderSymbol // 内部的符号。加载器符号表
	loaderReloc     []*xcoffLoaderReloc  // 必须在加载器
	sync.Mutex                           // 当前保护加载器Reloc 
}

// XCOFF生成算法
var (
	xfile xcoffFile
)

// xcoffStringTable使用的Var是一个XCOFF字符串表。
type xcoffStringTable struct {
	strings    []string
	stringsLen int
}

// size返回字符串表t的大小。
func (t *xcoffStringTable) size() int {
	// 字符串表以4字节的长度开头
	return t.stringsLen + 4
}

// add将字符串str添加到字符串表t中。
func (t *xcoffStringTable) add(str string) int {
	off := t.size()
	t.strings = append(t.strings, str)
	t.stringsLen += len(str) + 1 // 每个字符串将附加0 
	return off
}

// 将字符串表t写入输出文件。
func (t *xcoffStringTable) write(out *OutBuf) {
	out.Write32(uint32(t.size()))
	for _, s := range t.strings {
		out.WriteString(s)
		out.Write8(0)
	}
}

// 将XCOFF节写入输出文件。
func (sect *XcoffScnHdr64) write(ctxt *Link) {
	binary.Write(ctxt.Out, binary.BigEndian, sect)
	ctxt.Out.Write32(0) // 在末尾添加4个空字节以匹配对齐方式
}

// addSection向XCOFF文件f添加节。
func (f *xcoffFile) addSection(name string, addr uint64, size uint64, fileoff uint64, flags uint32) *XcoffScnHdr64 {
	sect := &XcoffScnHdr64{
		Spaddr:  addr,
		Svaddr:  addr,
		Ssize:   size,
		Sscnptr: fileoff,
		Sflags:  flags,
	}
	copy(sect.Sname[:], name) // 将字符串复制到[8]字节
	f.sections = append(f.sections, sect)
	f.sectNameToScnum[name] = int16(len(f.sections))
	return sect
}

// addDwarfSection向XCOFF文件f添加一个矮节。
// 此函数类似于addSection，但是侏儒组的名称
// 必须修改为常规名称，并且它们是不同的子类型。
func (f *xcoffFile) addDwarfSection(s *sym.Section) *XcoffScnHdr64 {
	newName, subtype := xcoffGetDwarfSubtype(s.Name)
	return f.addSection(newName, 0, s.Length, s.Seg.Fileoff+s.Vaddr-s.Seg.Vaddr, STYP_DWARF|subtype)
}

// XCOFFGetDWARFSUBYPE返回DWARF段str 
// 的XCOFF名称及其子类型常量。
func xcoffGetDwarfSubtype(str string) (string, uint32) {
	switch str {
	default:
		Exitf("unknown DWARF section name for XCOFF: %s", str)
	case ".debug_abbrev":
		return ".dwabrev", SSUBTYP_DWABREV
	case ".debug_info":
		return ".dwinfo", SSUBTYP_DWINFO
	case ".debug_frame":
		return ".dwframe", SSUBTYP_DWFRAME
	case ".debug_line":
		return ".dwline", SSUBTYP_DWLINE
	case ".debug_loc":
		return ".dwloc", SSUBTYP_DWLOC
	case ".debug_pubnames":
		return ".dwpbnms", SSUBTYP_DWPBNMS
	case ".debug_pubtypes":
		return ".dwpbtyp", SSUBTYP_DWPBTYP
	case ".debug_ranges":
		return ".dwrnges", SSUBTYP_DWRNGES
	}
	// 从未使用过
	return "", 0
}

// GetXCoffCnum返回Go段的XCOFF段号。
func (f *xcoffFile) getXCOFFscnum(sect *sym.Section) int16 {
	switch sect.Seg {
	case &Segtext:
		return f.sectNameToScnum[".text"]
	case &Segdata:
		if sect.Name == ".noptrbss" || sect.Name == ".bss" {
			return f.sectNameToScnum[".bss"]
		}
		if sect.Name == ".tbss" {
			return f.sectNameToScnum[".tbss"]
		}
		return f.sectNameToScnum[".data"]
	case &Segdwarf:
		name, _ := xcoffGetDwarfSubtype(sect.Name)
		return f.sectNameToScnum[name]
	case &Segrelrodata:
		return f.sectNameToScnum[".data"]
	}
	Errorf(nil, "getXCOFFscnum not implemented for section %s", sect.Name)
	return -1
}

// Xcoffinit初始化了一些内部值并设置了
// 已知的头信息
func Xcoffinit(ctxt *Link) {
	xfile.dynLibraries = make(map[string]int)

	HEADR = int32(Rnd(XCOFFHDRRESERVE, XCOFFSECTALIGN))
	if *FlagTextAddr != -1 {
		Errorf(nil, "-T not available on AIX")
	}
	*FlagTextAddr = XCOFFTEXTBASE + int64(HEADR)
	if *FlagRound != -1 {
		Errorf(nil, "-R not available on AIX")
	}
	*FlagRound = int(XCOFFSECTALIGN)

}

// 符号表

// 在XCOFF中键入genasmsym所需的C_文件信息。
type xcoffSymSrcFile struct {
	name         string
	file         *XcoffSymEnt64   // 此C_文件的符号
	csectAux     *XcoffAuxCSect64 // 当前文件的符号。csect 
	csectSymNb   uint64           // 当前的符号号。csect 
	csectVAStart int64
	csectVAEnd   int64
}

var (
	currDwscnoff   = make(map[string]uint64) // 需要创建C_矮人符号
	currSymSrcFile xcoffSymSrcFile
	outerSymSize   = make(map[string]int64)
)

// xcoffUpdateOuterSize存储外部符号的大小，以便将其
// 保存在符号表中。
func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
	if size == 0 {
		return
	}
	// TODO:使用CarrierSymByType 

	ldr := ctxt.loader
	switch stype {
	default:
		Errorf(nil, "unknown XCOFF outer symbol for type %s", stype.String())
	case sym.SRODATA, sym.SRODATARELRO, sym.SFUNCTAB, sym.SSTRING:
		// 无需执行任何操作
	case sym.STYPERELRO:
		if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
			// 运行时。类型大小必须删除，因为它是一个真正的符号。
			tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
			outerSymSize["typerel.*"] = size - tsize
			return
		}
		fallthrough
	case sym.STYPE:
		if !ctxt.DynlinkingGo() {
			// 运行时。类型大小必须删除，因为它是一个真正的符号。
			tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
			outerSymSize["type.*"] = size - tsize
		}
	case sym.SGOSTRING:
		outerSymSize["go.string.*"] = size
	case sym.SGOFUNC:
		if !ctxt.DynlinkingGo() {
			outerSymSize["go.func.*"] = size
		}
	case sym.SGOFUNCRELRO:
		outerSymSize["go.funcrel.*"] = size
	case sym.SGCBITS:
		outerSymSize["runtime.gcbits.*"] = size
	case sym.SPCLNTAB:
		outerSymSize["runtime.pclntab"] = size
	}
}

// addSymbol在ctxt上写入一个符号或一个辅助符号条目。出来
func (f *xcoffFile) addSymbol(sym xcoffSym) {
	f.symtabSym = append(f.symtabSym, sym)
	f.symbolCount++
}

// xcoffAlign返回符号对齐的对数基数2。
func xcoffAlign(ldr *loader.Loader, x loader.Sym, t SymbolType) uint8 {
	align := ldr.SymAlign(x)
	if align == 0 {
		if t == TextSym {
			align = int32(Funcalign)
		} else {
			align = symalign(ldr, x)
		}
	}
	return logBase2(int(align))
}

// logBase2返回a.
func logBase2(a int) uint8 {
	return uint8(bits.Len(uint(a)) - 1)
}

// 新文件出现时所需的写符号：
// -一个C_文件，其名称带有一个辅助项
// -C_矮人符号以提供调试信息
// -一个C_隐藏文本，它将是一个包含其所有函数的csect 
// /它需要几个参数创造。csect符号，例如其入口点和段号。
// 
// 目前，一个新文件实际上是一个新包。看起来还可以，但将来可能会改变
// 。
func (f *xcoffFile) writeSymbolNewFile(ctxt *Link, name string, firstEntry uint64, extnum int16) {
	ldr := ctxt.loader
	/* C_FILE */
	s := &XcoffSymEnt64{
		Noffset: uint32(f.stringTable.add(".file")),
		Nsclass: C_FILE,
		Nscnum:  N_DEBUG,
		Ntype:   0, // Go不在预定义语言中。
		Nnumaux: 1,
	}
	f.addSymbol(s)
	currSymSrcFile.file = s

	// 文件名的辅助项。
	auxf := &XcoffAuxFile64{
		Xoffset:  uint32(f.stringTable.add(name)),
		Xftype:   XFT_FN,
		Xauxtype: _AUX_FILE,
	}
	f.addSymbol(auxf)

	/* Dwarf */
	for _, sect := range Segdwarf.Sections {
		var dwsize uint64
		if ctxt.LinkMode == LinkInternal {
			// 查找此对应的包矮人编译单元的大小。
			// 这个尺寸是在矮人世代设定的（参见DWARF.go）。
			dwsize = getDwsectCUSize(sect.Name, name)
			// /。debug_abbrev对所有软件包都是通用的，在前面的函数
			if sect.Name == ".debug_abbrev" {
				dwsize = uint64(ldr.SymSize(loader.Sym(sect.Sym)))

			}
		} else {
			// 只有一个。带有外部链接的文件。
			dwsize = sect.Length
		}

		// 获取XCOFF名称
		name, _ := xcoffGetDwarfSubtype(sect.Name)
		s := &XcoffSymEnt64{
			Nvalue:  currDwscnoff[sect.Name],
			Noffset: uint32(f.stringTable.add(name)),
			Nsclass: C_DWARF,
			Nscnum:  f.getXCOFFscnum(sect),
			Nnumaux: 1,
		}

		if currSymSrcFile.csectAux == nil {
			// 矮人重新定位需要的符号号。dw*符号。
			// 不需要为每个包都知道它，一个就足够了。
			// currSymSrcFile。csectAux==nil表示第一个包。
			ldr.SetSymDynid(loader.Sym(sect.Sym), int32(f.symbolCount))

			if sect.Name == ".debug_frame" && ctxt.LinkMode != LinkExternal {
				// 必须将CIE大小添加到第一个包中。
				dwsize += 48
			}
		}

		f.addSymbol(s)

		// 更新此文件中的侏儒段偏移量
		if sect.Name != ".debug_abbrev" {
			currDwscnoff[sect.Name] += dwsize
		}

		// 辅助侏儒段
		auxd := &XcoffAuxDWARF64{
			Xscnlen:  dwsize,
			Xauxtype: _AUX_SECT,
		}

		f.addSymbol(auxd)
	}

	/* .csect */
	// 检查extnum是否在文本中。
	// 这是暂时的，只是为了检查这个算法是否正确。
	if extnum != 1 {
		Exitf("XCOFF symtab: A new file was detected with its first symbol not in .text")
	}

	currSymSrcFile.csectSymNb = uint64(f.symbolCount)

	// 没有偏移，因为没有名字
	s = &XcoffSymEnt64{
		Nvalue:  firstEntry,
		Nscnum:  extnum,
		Nsclass: C_HIDEXT,
		Ntype:   0, // 检查可见性？
		Nnumaux: 1,
	}
	f.addSymbol(s)

	aux := &XcoffAuxCSect64{
		Xsmclas:  XMC_PR,
		Xsmtyp:   XTY_SD | logBase2(Funcalign)<<3,
		Xauxtype: _AUX_CSECT,
	}
	f.addSymbol(aux)

	currSymSrcFile.csectAux = aux
	currSymSrcFile.csectVAStart = int64(firstEntry)
	currSymSrcFile.csectVAEnd = int64(firstEntry)
}

// 更新上一个包的值。
// /-C_文件符号的值：如果是最后一个，该值必须是csect符号的-1 
// /-Xsclen。
func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) {
	// 第一个文件
	if currSymSrcFile.file == nil {
		return
	}

	// 更新C_文件
	cfile := currSymSrcFile.file
	if last {
		cfile.Nvalue = 0xFFFFFFFFFFFFFFFF
	} else {
		cfile.Nvalue = uint64(f.symbolCount)
	}

	// 更新此辅助条目中的csect scnlen 
	aux := currSymSrcFile.csectAux
	csectSize := currSymSrcFile.csectVAEnd - currSymSrcFile.csectVAStart
	aux.Xscnlenlo = uint32(csectSize & 0xFFFFFFFF)
	aux.Xscnlenhi = uint32(csectSize >> 32)
}

// 写入代表。文本功能。
// 符号表用对应于每个包的C_文件分割，而不是按原样分割到每个源文件。
func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x loader.Sym) []xcoffSym {
	// 将写入的新XCOFF符号。
	syms := []xcoffSym{}

	// 检查是否检测到新文件。
	ldr := ctxt.loader
	name := ldr.SymName(x)
	if strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") {
		// 蹦床没有文件，因此当前文件中有
		// 。
		// 运行时也是如此。文本X符号。
	} else if ldr.SymPkg(x) == "" { // 未定义的全局符号
		// 如果发生这种情况，必须重新执行算法。
		if currSymSrcFile.name != "" {
			Exitf("undefined global symbol found inside another file")
		}
	} else {
		// 当前文件已更改。必须生成新的C_文件、C_DWARF等。
		if currSymSrcFile.name != ldr.SymPkg(x) {
			if ctxt.LinkMode == LinkInternal {
				// 使用外部链接更新以前的文件值
				xfile.updatePreviousFile(ctxt, false)
				currSymSrcFile.name = ldr.SymPkg(x)
				f.writeSymbolNewFile(ctxt, ldr.SymPkg(x), uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x)))
			} else {
				// 如果有多个
				// ，ld将崩溃。文件和DWARF调试在重新定位阶段的某个地方启用。
				// 因此，所有软件包都合并在一个假文件下。文件
				// /“go_函数”。
				// TODO（aix）；一旦ld被修复或触发
				// 重新定位被发现并修复，则移除。
				if currSymSrcFile.name == "" {
					currSymSrcFile.name = ldr.SymPkg(x)
					f.writeSymbolNewFile(ctxt, "go_functions", uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x)))
				}
			}

		}
	}

	name = ldr.SymExtname(x)
	name = mangleABIName(ctxt, ldr, x, name)

	s := &XcoffSymEnt64{
		Nsclass: C_EXT,
		Noffset: uint32(xfile.stringTable.add(name)),
		Nvalue:  uint64(ldr.SymValue(x)),
		Nscnum:  f.getXCOFFscnum(ldr.SymSect(x)),
		Ntype:   SYM_TYPE_FUNC,
		Nnumaux: 2,
	}

	if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
		s.Nsclass = C_HIDEXT
	}

	ldr.SetSymDynid(x, int32(xfile.symbolCount))
	syms = append(syms, s)

	// 通过跟踪VA范围来跟踪截面尺寸。单个
	// 对齐差异可能会引入几个额外字节的填充
	// ldr无法完全解释这些内容。SymSize（x）。
	sv := ldr.SymValue(x) + ldr.SymSize(x)
	if currSymSrcFile.csectVAEnd < sv {
		currSymSrcFile.csectVAEnd = sv
	}

	// 创建辅助条目
	a2 := &XcoffAuxFcn64{
		Xfsize:   uint32(ldr.SymSize(x)),
		Xlnnoptr: 0,                     // TODO 
		Xendndx:  xfile.symbolCount + 3, // 此符号+2个辅助条目
		Xauxtype: _AUX_FCN,
	}
	syms = append(syms, a2)

	a4 := &XcoffAuxCSect64{
		Xscnlenlo: uint32(currSymSrcFile.csectSymNb & 0xFFFFFFFF),
		Xscnlenhi: uint32(currSymSrcFile.csectSymNb >> 32),
		Xsmclas:   XMC_PR, // 程序代码
		Xsmtyp:    XTY_LD, // 标签定义（基于C）
		Xauxtype:  _AUX_CSECT,
	}
	a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3)

	syms = append(syms, a4)
	return syms
}

// genasmsym用于写入符号表
func putaixsym(ctxt *Link, x loader.Sym, t SymbolType) {
	// 此GO生成的所有XCOFF符号
	// 可以是符号条目或文件中的辅助项
	syms := []xcoffSym{}

	ldr := ctxt.loader
	name := ldr.SymName(x)
	if t == UndefinedSym {
		name = ldr.SymExtname(x)
	}

	switch t {
	default:
		return

	case TextSym:
		if ldr.SymPkg(x) != "" || strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") {
			// 函数
			syms = xfile.writeSymbolFunc(ctxt, x)
		} else {
			// 仅限运行时。文本和运行时。etext通过这种方式
			if name != "runtime.text" && name != "runtime.etext" && name != "go.buildid" {
				Exitf("putaixsym: unknown text symbol %s", name)
			}
			s := &XcoffSymEnt64{
				Nsclass: C_HIDEXT,
				Noffset: uint32(xfile.stringTable.add(name)),
				Nvalue:  uint64(ldr.SymValue(x)),
				Nscnum:  xfile.getXCOFFscnum(ldr.SymSect(x)),
				Ntype:   SYM_TYPE_FUNC,
				Nnumaux: 1,
			}
			ldr.SetSymDynid(x, int32(xfile.symbolCount))
			syms = append(syms, s)

			size := uint64(ldr.SymSize(x))
			a4 := &XcoffAuxCSect64{
				Xauxtype:  _AUX_CSECT,
				Xscnlenlo: uint32(size & 0xFFFFFFFF),
				Xscnlenhi: uint32(size >> 32),
				Xsmclas:   XMC_PR,
				Xsmtyp:    XTY_SD,
			}
			a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3)
			syms = append(syms, a4)
		}

	case DataSym, BSSSym:
		s := &XcoffSymEnt64{
			Nsclass: C_EXT,
			Noffset: uint32(xfile.stringTable.add(name)),
			Nvalue:  uint64(ldr.SymValue(x)),
			Nscnum:  xfile.getXCOFFscnum(ldr.SymSect(x)),
			Nnumaux: 1,
		}

		if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
			// 在全局数据
			// 的情况下，有更多符号与程序集生成的
			// 有关，以访问这些符号。
			// 但是作为一种检查符号是
			// 全局还是局部（大写字母）的方式，我们还不需要
			// 实现它们。
			s.Nsclass = C_HIDEXT
		}

		ldr.SetSymDynid(x, int32(xfile.symbolCount))
		syms = append(syms, s)

		// 创建辅助条目

		// 通常，大小应该是包含所有
		// 一个文件/包的数据和bss符号的csect的大小。
		// 然而，每个符号都有一个csect比较容易。
		// 在外部链接过程中可能会更改
		size := uint64(ldr.SymSize(x))
		a4 := &XcoffAuxCSect64{
			Xauxtype:  _AUX_CSECT,
			Xscnlenlo: uint32(size & 0xFFFFFFFF),
			Xscnlenhi: uint32(size >> 32),
		}

		if ty := ldr.SymType(x); ty >= sym.STYPE && ty <= sym.SPCLNTAB {
			if ctxt.IsExternal() && strings.HasPrefix(ldr.SymSect(x).Name, ".data.rel.ro") {
				// 带有重新定位
				// 的只读数据必须在。数据
				a4.Xsmclas = XMC_RW
			} else {
				// 只读数据
				a4.Xsmclas = XMC_RO
			}
		} else if /*ty == sym.SDATA &&*/ strings.HasPrefix(ldr.SymName(x), "TOC.") && ctxt.IsExternal() {
			a4.Xsmclas = XMC_TC
		} else if ldr.SymName(x) == "TOC" {
			a4.Xsmclas = XMC_TC0
		} else {
			a4.Xsmclas = XMC_RW
		}
		if t == DataSym {
			a4.Xsmtyp |= XTY_SD
		} else {
			a4.Xsmtyp |= XTY_CM
		}

		a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, t) << 3)

		syms = append(syms, a4)

	case UndefinedSym:
		if ty := ldr.SymType(x); ty != sym.SDYNIMPORT && ty != sym.SHOSTOBJ && ty != sym.SUNDEFEXT {
			return
		}
		s := &XcoffSymEnt64{
			Nsclass: C_EXT,
			Noffset: uint32(xfile.stringTable.add(name)),
			Nnumaux: 1,
		}
		ldr.SetSymDynid(x, int32(xfile.symbolCount))
		syms = append(syms, s)

		a4 := &XcoffAuxCSect64{
			Xauxtype: _AUX_CSECT,
			Xsmclas:  XMC_DS,
			Xsmtyp:   XTY_ER | XTY_IMP,
		}

		if ldr.SymName(x) == "__n_pthreads" {
			// 目前，cgo_import_dynamic生成的所有导入符号都是
			// 系统调用函数，但u n_pthreads是一个变量。
			// TODO（aix）：找到一种检测cgo导入的变量的方法。
			a4.Xsmclas = XMC_RW
		}

		syms = append(syms, a4)

	case TLSSym:
		s := &XcoffSymEnt64{
			Nsclass: C_EXT,
			Noffset: uint32(xfile.stringTable.add(name)),
			Nscnum:  xfile.getXCOFFscnum(ldr.SymSect(x)),
			Nvalue:  uint64(ldr.SymValue(x)),
			Nnumaux: 1,
		}

		ldr.SetSymDynid(x, int32(xfile.symbolCount))
		syms = append(syms, s)

		size := uint64(ldr.SymSize(x))
		a4 := &XcoffAuxCSect64{
			Xauxtype:  _AUX_CSECT,
			Xsmclas:   XMC_UL,
			Xsmtyp:    XTY_CM,
			Xscnlenlo: uint32(size & 0xFFFFFFFF),
			Xscnlenhi: uint32(size >> 32),
		}

		syms = append(syms, a4)
	}

	for _, s := range syms {
		xfile.addSymbol(s)
	}
}

// 生成XCOFF符号表。
// 它将在Asmbxcoff中写入out文件，因为它必须在最后是
// 尤其是在需要符号索引的重新定位部分之后。
func (f *xcoffFile) asmaixsym(ctxt *Link) {
	ldr := ctxt.loader
	// 为包装其他符号（如围棋）的符号获取正确的大小。字符串。*
	// sym。大小可以直接使用，因为符号已经被写入。这些符号不会出现在下面的第一个循环中，因为我们跳过了符号。STEXT符号。正常症状。STEXT符号由行走的textp发出。
	for name, size := range outerSymSize {
		sym := ldr.Lookup(name, 0)
		if sym == 0 {
			Errorf(nil, "unknown outer symbol with name %s", name)
		} else {
			s := ldr.MakeSymbolUpdater(sym)
			s.SetSize(size)
		}
	}

	s := ldr.Lookup("runtime.text", 0)
	if ldr.SymType(s) == sym.STEXT {
		// 我们已经在ctxt中包含了这个符号。带有外部链接器的AIX上的Textp。
		// 参见数据。go:/textaddress 
		if !ctxt.IsExternal() {
			putaixsym(ctxt, s, TextSym)
		}
	}

	n := 1
	// 如果有多个
	for _, sect := range Segtext.Sections[1:] {
		if sect.Name != ".text" || ctxt.IsExternal() {
			// 在AIX上，运行时。文本X是符号选项卡中已有的符号。
			break
		}
		s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
		if s == 0 {
			break
		}
		if ldr.SymType(s) == sym.STEXT {
			putaixsym(ctxt, s, TextSym)
		}
		n++
	}

	s = ldr.Lookup("runtime.etext", 0)
	if ldr.SymType(s) == sym.STEXT {
		// 我们已经在ctxt中包含了这个符号。Textp 
		// 在带有外部链接器的AIX上。
		// 参见数据。go:/textaddress 
		if !ctxt.IsExternal() {
			putaixsym(ctxt, s, TextSym)
		}
	}

	shouldBeInSymbolTable := func(s loader.Sym, name string) bool {
		if name == ".go.buildinfo" {
			// 在AIX上。去buildinfo必须以
			// /的形式出现在符号表中。它有重定位。
			return true
		}
		if ldr.AttrNotInSymbolTable(s) {
			return false
		}
		if (name == "" || name[0] == '.') && !ldr.IsFileLocal(s) && name != ".TOC." {
			return false
		}
		return true
	}

	for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ {
		if !shouldBeInSymbolTable(s, ldr.SymName(s)) {
			continue
		}
		st := ldr.SymType(s)
		switch {
		case st == sym.STLSBSS:
			if ctxt.IsExternal() {
				putaixsym(ctxt, s, TLSSym)
			}

		case st == sym.SBSS, st == sym.SNOPTRBSS, st == sym.SLIBFUZZER_EXTRA_COUNTER:
			if ldr.AttrReachable(s) {
				data := ldr.Data(s)
				if len(data) > 0 {
					ldr.Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(data), ldr.SymType(s), ldr.AttrSpecial(s))
				}
				putaixsym(ctxt, s, BSSSym)
			}

		case st >= sym.SELFRXSECT && st < sym.SXREF: // 在dodata中处理的数据节
			if ldr.AttrReachable(s) {
				putaixsym(ctxt, s, DataSym)
			}

		case st == sym.SUNDEFEXT:
			putaixsym(ctxt, s, UndefinedSym)

		case st == sym.SDYNIMPORT:
			if ldr.AttrReachable(s) {
				putaixsym(ctxt, s, UndefinedSym)
			}
		}
	}

	for _, s := range ctxt.Textp {
		putaixsym(ctxt, s, TextSym)
	}

	if ctxt.Debugvlog != 0 || *flagN {
		ctxt.Logf("symsize = %d\n", uint32(symSize))
	}
	xfile.updatePreviousFile(ctxt, true)
}

func (f *xcoffFile) genDynSym(ctxt *Link) {
	ldr := ctxt.loader
	var dynsyms []loader.Sym
	for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
		if !ldr.AttrReachable(s) {
			continue
		}
		if t := ldr.SymType(s); t != sym.SHOSTOBJ && t != sym.SDYNIMPORT {
			continue
		}
		dynsyms = append(dynsyms, s)
	}

	for _, s := range dynsyms {
		f.adddynimpsym(ctxt, s)

		if _, ok := f.dynLibraries[ldr.SymDynimplib(s)]; !ok {
			f.dynLibraries[ldr.SymDynimplib(s)] = len(f.dynLibraries)
		}
	}
}

// /（*xcoffFile）adddynimpsym将动态符号“s”添加到XCOFF文件中。
// 将创建一个名为s.Extname（）的新符号，作为中的实际动态符号
// 。加载器部分和符号表中作为外部参考。
// 符号“s”被转换为SXCOFFTOC，以。数据部分。
// 但是，这些符号没有文字保护，可能需要添加。
// TODO（aix）：在没有库的情况下处理动态符号。
func (f *xcoffFile) adddynimpsym(ctxt *Link, s loader.Sym) {
	// 检查是否给出了库名。
	// 编译时已检查模式。
	ldr := ctxt.loader
	if ctxt.IsInternal() && ldr.SymDynimplib(s) == "" {
		ctxt.Errorf(s, "imported symbol must have a given library")
	}

	sb := ldr.MakeSymbolUpdater(s)
	sb.SetReachable(true)
	sb.SetType(sym.SXCOFFTOC)

	// 创建新的动态符号
	extsym := ldr.CreateSymForUpdate(ldr.SymExtname(s), 0)
	extsym.SetType(sym.SDYNIMPORT)
	extsym.SetDynimplib(ldr.SymDynimplib(s))
	extsym.SetExtname(ldr.SymExtname(s))
	extsym.SetDynimpvers(ldr.SymDynimpvers(s))

	// 添加加载程序符号
	lds := &xcoffLoaderSymbol{
		sym:    extsym.Sym(),
		smtype: XTY_IMP,
		smclas: XMC_DS,
	}
	if ldr.SymName(s) == "__n_pthreads" {
		// 目前，cgo_import_dynamic生成的所有导入符号都是
		// 系统调用函数，但作为变量的_un_pthreads除外。
		// TODO（aix）：找到一种检测cgo导入的变量的方法。
		lds.smclas = XMC_RW
	}
	f.loaderSymbols = append(f.loaderSymbols, lds)

	// 重新定位以检索外部地址
	sb.AddBytes(make([]byte, 8))
	r, _ := sb.AddRel(objabi.R_ADDR)
	r.SetSym(extsym.Sym())
	r.SetSiz(uint8(ctxt.Arch.PtrSize))
	// TODO:可能是
	// sb。设置大小（0）
	// sb。SetData（无）
	// sb。addr（ctxt.Arch，extsym.Sym（））
	// 如果大小一开始不是0，我认为没有必要添加8字节
	// 的零。
}

// Xcoffadddynrel在XCOFF文件中添加动态重定位。
// 此重新定位将由装载机进行。
func Xcoffadddynrel(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
	if target.IsExternal() {
		return true
	}
	if ldr.SymType(s) <= sym.SPCLNTAB {
		ldr.Errorf(s, "cannot have a relocation to %s in a text section symbol", ldr.SymName(r.Sym()))
		return false
	}

	xldr := &xcoffLoaderReloc{
		sym:  s,
		roff: r.Off(),
	}
	targ := r.Sym()
	var targType sym.SymKind
	if targ != 0 {
		targType = ldr.SymType(targ)
	}

	switch r.Type() {
	default:
		ldr.Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", ldr.SymName(targ), r.Type().String())
		return false
	case objabi.R_ADDR:
		if ldr.SymType(s) == sym.SXCOFFTOC && targType == sym.SDYNIMPORT {
			// 导入符号重新定位
			for i, dynsym := range xfile.loaderSymbols {
				if ldr.SymName(dynsym.sym) == ldr.SymName(targ) {
					xldr.symndx = int32(i + 3) // /+3，因为有3个部分符号
					break
				}
			}
		} else if t := ldr.SymType(s); t == sym.SDATA || t == sym.SNOPTRDATA || t == sym.SBUILDINFO || t == sym.SXCOFFTOC {
			switch ldr.SymSect(targ).Seg {
			default:
				ldr.Errorf(s, "unknown segment for .loader relocation with symbol %s", ldr.SymName(targ))
			case &Segtext:
			case &Segrodata:
				xldr.symndx = 0 // /。文本
			case &Segdata:
				if targType == sym.SBSS || targType == sym.SNOPTRBSS {
					xldr.symndx = 2 // /。bss 
				} else {
					xldr.symndx = 1 // /。数据
				}
			}

		} else {
			ldr.Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", ldr.SymName(targ), ldr.SymType(s), ldr.SymType(targ))
			return false
		}

		xldr.rtype = 0x3F<<8 + XCOFF_R_POS
	}

	xfile.Lock()
	xfile.loaderReloc = append(xfile.loaderReloc, xldr)
	xfile.Unlock()
	return true
}

func (ctxt *Link) doxcoff() {
	ldr := ctxt.loader

	// TOC 
	toc := ldr.CreateSymForUpdate("TOC", 0)
	toc.SetType(sym.SXCOFFTOC)
	toc.SetVisibilityHidden(true)

	// 向添加入口点。加载器符号。
	ep := ldr.Lookup(*flagEntrySymbol, 0)
	if ep == 0 || !ldr.AttrReachable(ep) {
		Exitf("wrong entry point")
	}

	xfile.loaderSymbols = append(xfile.loaderSymbols, &xcoffLoaderSymbol{
		sym:    ep,
		smtype: XTY_ENT | XTY_SD,
		smclas: XMC_DS,
	})

	xfile.genDynSym(ctxt)

	for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
		if strings.HasPrefix(ldr.SymName(s), "TOC.") {
			sb := ldr.MakeSymbolUpdater(s)
			sb.SetType(sym.SXCOFFTOC)
		}
	}

	if ctxt.IsExternal() {
		// 在运行时/cgo:main（）将rt0_go名称更改为匹配名称。
		rt0 := ldr.Lookup("runtime.rt0_go", 0)
		ldr.SetSymExtname(rt0, "runtime_rt0_go")

		nsym := loader.Sym(ldr.NSym())
		for s := loader.Sym(1); s < nsym; s++ {
			if !ldr.AttrCgoExport(s) {
				continue
			}
			if ldr.IsFileLocal(s) {
				panic("cgo_export on static symbol")
			}

			if ldr.SymType(s) == sym.STEXT {
				// 在AIX上，导出的函数必须有两个符号：
				// /-a。必须以“.”开头的文本符号。
				// /-a。作为函数描述符的数据符号。
				name := ldr.SymExtname(s)
				ldr.SetSymExtname(s, "."+name)

				desc := ldr.MakeSymbolUpdater(ldr.CreateExtSym(name, 0))
				desc.SetReachable(true)
				desc.SetType(sym.SNOPTRDATA)
				desc.AddAddr(ctxt.Arch, s)
				desc.AddAddr(ctxt.Arch, toc.Sym())
				desc.AddUint64(ctxt.Arch, 0)
			}
		}
	}
}

// 加载程序段
// 当前，根据xfile对象中检索到的信息，在组装XCOFF文件
// 时从头创建此段。

// 创建加载器节并返回其大小
func Loaderblk(ctxt *Link, off uint64) {
	xfile.writeLdrScn(ctxt, off)
}

func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) {
	var symtab []*XcoffLdSym64
	var strtab []*XcoffLdStr64
	var importtab []*XcoffLdImportFile64
	var reloctab []*XcoffLdRel64
	var dynimpreloc []*XcoffLdRel64

	// 当字符串表在任何加载器子节中更新时，
	// 必须同时计算其长度。
	stlen := uint32(0)

	// 加载头
	hdr := &XcoffLdHdr64{
		Lversion: 2,
		Lsymoff:  LDHDRSZ_64,
	}

	ldr := ctxt.loader
	/* Symbol table */
	for _, s := range f.loaderSymbols {
		lds := &XcoffLdSym64{
			Loffset: uint32(stlen + 2),
			Lsmtype: s.smtype,
			Lsmclas: s.smclas,
		}
		sym := s.sym
		switch s.smtype {
		default:
			ldr.Errorf(sym, "unexpected loader symbol type: 0x%x", s.smtype)
		case XTY_ENT | XTY_SD:
			lds.Lvalue = uint64(ldr.SymValue(sym))
			lds.Lscnum = f.getXCOFFscnum(ldr.SymSect(sym))
		case XTY_IMP:
			lds.Lifile = int32(f.dynLibraries[ldr.SymDynimplib(sym)] + 1)
		}
		ldstr := &XcoffLdStr64{
			size: uint16(len(ldr.SymName(sym)) + 1), // /+null终止符
			name: ldr.SymName(sym),
		}
		stlen += uint32(2 + ldstr.size) // /2=sizeof ldstr。大小
		symtab = append(symtab, lds)
		strtab = append(strtab, ldstr)

	}

	hdr.Lnsyms = int32(len(symtab))
	hdr.Lrldoff = hdr.Lsymoff + uint64(24*hdr.Lnsyms) // 24=SIZEO of one symbol 
	off := hdr.Lrldoff                                // 当前偏移量与reloc offset 

	/* Reloc */
	// 确保确定顺序
	sort.Slice(f.loaderReloc, func(i, j int) bool {
		r1, r2 := f.loaderReloc[i], f.loaderReloc[j]
		if r1.sym != r2.sym {
			return r1.sym < r2.sym
		}
		if r1.roff != r2.roff {
			return r1.roff < r2.roff
		}
		if r1.rtype != r2.rtype {
			return r1.rtype < r2.rtype
		}
		return r1.symndx < r2.symndx
	})

	ep := ldr.Lookup(*flagEntrySymbol, 0)
	xldr := &XcoffLdRel64{
		Lvaddr:  uint64(ldr.SymValue(ep)),
		Lrtype:  0x3F00,
		Lrsecnm: f.getXCOFFscnum(ldr.SymSect(ep)),
		Lsymndx: 0,
	}
	off += 16
	reloctab = append(reloctab, xldr)

	off += uint64(16 * len(f.loaderReloc))
	for _, r := range f.loaderReloc {
		symp := r.sym
		if symp == 0 {
			panic("unexpected 0 sym value")
		}
		xldr = &XcoffLdRel64{
			Lvaddr:  uint64(ldr.SymValue(symp) + int64(r.roff)),
			Lrtype:  r.rtype,
			Lsymndx: r.symndx,
		}

		if ldr.SymSect(symp) != nil {
			xldr.Lrsecnm = f.getXCOFFscnum(ldr.SymSect(symp))
		}

		reloctab = append(reloctab, xldr)
	}

	off += uint64(16 * len(dynimpreloc))
	reloctab = append(reloctab, dynimpreloc...)

	hdr.Lnreloc = int32(len(reloctab))
	hdr.Limpoff = off

	/* Import */
	// 默认导入：/usr/lib:/lib 
	ldimpf := &XcoffLdImportFile64{
		Limpidpath: "/usr/lib:/lib",
	}
	off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // /+空分隔符
	importtab = append(importtab, ldimpf)

	// adddynimpsym创建的映射将名称与数字
	// 此数字代表库索引（-1）在这个导入文件部分
	// 因此，它们必须在放入部分
	libsOrdered := make([]string, len(f.dynLibraries))
	for key, val := range f.dynLibraries {
		if libsOrdered[val] != "" {
			continue
		}
		libsOrdered[val] = key
	}

	for _, lib := range libsOrdered {
		// 之前进行排序。库字符串被定义为基。a/mem。o或路径/基础。a/mem。o 
		n := strings.Split(lib, "/")
		path := ""
		base := n[len(n)-2]
		mem := n[len(n)-1]
		if len(n) > 2 {
			path = lib[:len(lib)-len(base)-len(mem)-2]

		}
		ldimpf = &XcoffLdImportFile64{
			Limpidpath: path,
			Limpidbase: base,
			Limpidmem:  mem,
		}
		off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // /+空分隔符
		importtab = append(importtab, ldimpf)
	}

	hdr.Lnimpid = int32(len(importtab))
	hdr.Listlen = uint32(off - hdr.Limpoff)
	hdr.Lstoff = off
	hdr.Lstlen = stlen

	/* Writing */
	ctxt.Out.SeekSet(int64(globalOff))
	binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, hdr)

	for _, s := range symtab {
		binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)

	}
	for _, r := range reloctab {
		binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, r)
	}
	for _, f := range importtab {
		ctxt.Out.WriteString(f.Limpidpath)
		ctxt.Out.Write8(0)
		ctxt.Out.WriteString(f.Limpidbase)
		ctxt.Out.Write8(0)
		ctxt.Out.WriteString(f.Limpidmem)
		ctxt.Out.Write8(0)
	}
	for _, s := range strtab {
		ctxt.Out.Write16(s.size)
		ctxt.Out.WriteString(s.name)
		ctxt.Out.Write8(0) // 空终止符
	}

	f.loaderSize = off + uint64(stlen)
}

// XCOFF汇编和写入文件

func (f *xcoffFile) writeFileHeader(ctxt *Link) {
	// 文件头
	f.xfhdr.Fmagic = U64_TOCMAGIC
	f.xfhdr.Fnscns = uint16(len(f.sections))
	f.xfhdr.Ftimedat = 0

	if !*FlagS {
		f.xfhdr.Fsymptr = uint64(f.symtabOffset)
		f.xfhdr.Fnsyms = int32(f.symbolCount)
	}

	if ctxt.BuildMode == BuildModeExe && ctxt.LinkMode == LinkInternal {
		ldr := ctxt.loader
		f.xfhdr.Fopthdr = AOUTHSZ_EXEC64
		f.xfhdr.Fflags = F_EXEC

		// 辅助头
		f.xahdr.Ovstamp = 1 // 基于转储-o 
		f.xahdr.Omagic = 0x10b
		copy(f.xahdr.Omodtype[:], "1L")
		entry := ldr.Lookup(*flagEntrySymbol, 0)
		f.xahdr.Oentry = uint64(ldr.SymValue(entry))
		f.xahdr.Osnentry = f.getXCOFFscnum(ldr.SymSect(entry))
		toc := ldr.Lookup("TOC", 0)
		f.xahdr.Otoc = uint64(ldr.SymValue(toc))
		f.xahdr.Osntoc = f.getXCOFFscnum(ldr.SymSect(toc))

		f.xahdr.Oalgntext = int16(logBase2(int(XCOFFSECTALIGN)))
		f.xahdr.Oalgndata = 0x5

		binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
		binary.Write(ctxt.Out, binary.BigEndian, &f.xahdr)
	} else {
		f.xfhdr.Fopthdr = 0
		binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
	}

}

func xcoffwrite(ctxt *Link) {
	ctxt.Out.SeekSet(0)

	xfile.writeFileHeader(ctxt)

	for _, sect := range xfile.sections {
		sect.write(ctxt)
	}
}

// 生成XCOFF汇编文件
func asmbXcoff(ctxt *Link) {
	ctxt.Out.SeekSet(0)
	fileoff := int64(Segdwarf.Fileoff + Segdwarf.Filelen)
	fileoff = int64(Rnd(int64(fileoff), int64(*FlagRound)))

	xfile.sectNameToScnum = make(map[string]int16)

	// 添加节
	s := xfile.addSection(".text", Segtext.Vaddr, Segtext.Length, Segtext.Fileoff, STYP_TEXT)
	xfile.xahdr.Otextstart = s.Svaddr
	xfile.xahdr.Osntext = xfile.sectNameToScnum[".text"]
	xfile.xahdr.Otsize = s.Ssize
	xfile.sectText = s

	segdataVaddr := Segdata.Vaddr
	segdataFilelen := Segdata.Filelen
	segdataFileoff := Segdata.Fileoff
	segbssFilelen := Segdata.Length - Segdata.Filelen
	if len(Segrelrodata.Sections) > 0 {
		// 将relro段合并到数据段
		// relro数据位于AIX上的数据段内。
		segdataVaddr = Segrelrodata.Vaddr
		segdataFileoff = Segrelrodata.Fileoff
		segdataFilelen = Segdata.Vaddr + Segdata.Filelen - Segrelrodata.Vaddr
	}

	s = xfile.addSection(".data", segdataVaddr, segdataFilelen, segdataFileoff, STYP_DATA)
	xfile.xahdr.Odatastart = s.Svaddr
	xfile.xahdr.Osndata = xfile.sectNameToScnum[".data"]
	xfile.xahdr.Odsize = s.Ssize
	xfile.sectData = s

	s = xfile.addSection(".bss", segdataVaddr+segdataFilelen, segbssFilelen, 0, STYP_BSS)
	xfile.xahdr.Osnbss = xfile.sectNameToScnum[".bss"]
	xfile.xahdr.Obsize = s.Ssize
	xfile.sectBss = s

	if ctxt.LinkMode == LinkExternal {
		var tbss *sym.Section
		for _, s := range Segdata.Sections {
			if s.Name == ".tbss" {
				tbss = s
				break
			}
		}
		s = xfile.addSection(".tbss", tbss.Vaddr, tbss.Length, 0, STYP_TBSS)
	}

	// 添加矮段
	for _, sect := range Segdwarf.Sections {
		xfile.addDwarfSection(sect)
	}

	// 添加和写入剩余段
	if ctxt.LinkMode == LinkInternal {
		// 加载段
		if ctxt.BuildMode == BuildModeExe {
			Loaderblk(ctxt, uint64(fileoff))
			s = xfile.addSection(".loader", 0, xfile.loaderSize, uint64(fileoff), STYP_LOADER)
			xfile.xahdr.Osnloader = xfile.sectNameToScnum[".loader"]

			// 更新符号表
			fileoff += int64(xfile.loaderSize)
		}
	}

	// 创建符号表
	xfile.asmaixsym(ctxt)

	if ctxt.LinkMode == LinkExternal {
		xfile.emitRelocations(ctxt, fileoff)
	}

	// 写入符号表
	xfile.symtabOffset = ctxt.Out.Offset()
	for _, s := range xfile.symtabSym {
		binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)
	}
	// 写入字符串表
	xfile.stringTable.write(ctxt.Out)

	// 写入头
	xcoffwrite(ctxt)
}

// 重新定位go的搬迁条目。o外部链接。
func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) {
	ctxt.Out.SeekSet(fileoff)
	for ctxt.Out.Offset()&7 != 0 {
		ctxt.Out.Write8(0)
	}

	ldr := ctxt.loader
	// relocsect从sect中的第一个重新定位符号，并返回
	// 发出的重新定位总数。
	relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) uint32 {
		// ctxt。Logf（“%s 0x%x\n”，sect.Name，sect.Vaddr）
		// 如果主节没有位，则无需重新定位。
		if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
			return 0
		}
		sect.Reloff = uint64(ctxt.Out.Offset())
		for i, s := range syms {
			if !ldr.AttrReachable(s) {
				continue
			}
			if uint64(ldr.SymValue(s)) >= sect.Vaddr {
				syms = syms[i:]
				break
			}
		}
		eaddr := int64(sect.Vaddr + sect.Length)
		for _, s := range syms {
			if !ldr.AttrReachable(s) {
				continue
			}
			if ldr.SymValue(s) >= int64(eaddr) {
				break
			}

			// 在运行中计算外部重新定位，并传递到Xcoffreloc1以流出。
			// 重新定位必须按地址排序，所以创建一个排序索引列表。
			relocs := ldr.Relocs(s)
			sorted := make([]int, relocs.Count())
			for i := 0; i < relocs.Count(); i++ {
				sorted[i] = i
			}
			sort.Slice(sorted, func(i, j int) bool {
				return relocs.At(sorted[i]).Off() < relocs.At(sorted[j]).Off()
			})

			for _, ri := range sorted {
				r := relocs.At(ri)
				rr, ok := extreloc(ctxt, ldr, s, r)
				if !ok {
					continue
				}
				if rr.Xsym == 0 {
					ldr.Errorf(s, "missing xsym in relocation")
					continue
				}
				if ldr.SymDynid(rr.Xsym) < 0 {
					ldr.Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymDynid(rr.Xsym))
				}
				if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) {
					ldr.Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type(), r.Type(), r.Siz(), ldr.SymName(r.Sym()))
				}
			}
		}
		sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
		return uint32(sect.Rellen) / RELSZ_64
	}
	sects := []struct {
		xcoffSect *XcoffScnHdr64
		segs      []*sym.Segment
	}{
		{f.sectText, []*sym.Segment{&Segtext}},
		{f.sectData, []*sym.Segment{&Segrelrodata, &Segdata}},
	}
	for _, s := range sects {
		s.xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
		n := uint32(0)
		for _, seg := range s.segs {
			for _, sect := range seg.Sections {
				if sect.Name == ".text" {
					n += relocsect(sect, ctxt.Textp, 0)
				} else {
					n += relocsect(sect, ctxt.datap, 0)
				}
			}
		}
		s.xcoffSect.Snreloc += n
	}

dwarfLoop:
	for i := 0; i < len(Segdwarf.Sections); i++ {
		sect := Segdwarf.Sections[i]
		si := dwarfp[i]
		if si.secSym() != loader.Sym(sect.Sym) ||
			ldr.SymSect(si.secSym()) != sect {
			panic("inconsistency between dwarfp and Segdwarf")
		}
		for _, xcoffSect := range f.sections {
			_, subtyp := xcoffGetDwarfSubtype(sect.Name)
			if xcoffSect.Sflags&0xF0000 == subtyp {
				xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
				xcoffSect.Snreloc = relocsect(sect, si.syms, sect.Vaddr)
				continue dwarfLoop
			}
		}
		Errorf(nil, "emitRelocations: could not find %q section", sect.Name)
	}
}

// xcoffCreateExportFile为
// /-Wl，-bE选项创建带有导出符号的文件。
// ld不会导出符号，除非它们列在导出文件中。
func xcoffCreateExportFile(ctxt *Link) (fname string) {
	fname = filepath.Join(*flagTmpdir, "export_file.exp")
	var buf bytes.Buffer

	ldr := ctxt.loader
	for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ {
		if !ldr.AttrCgoExport(s) {
			continue
		}
		extname := ldr.SymExtname(s)
		if !strings.HasPrefix(extname, "._cgoexp_") {
			continue
		}
		if ldr.IsFileLocal(s) {
			continue // 仅导出非静态符号
		}

		// 检索cgo导出的初始符号
		// 的名称。
		// 对应的Go符号是：
		// /\u cgoexp\u hashcode\u symname。
		name := strings.SplitN(extname, "_", 4)[3]

		buf.Write([]byte(name + "\n"))
	}

	err := ioutil.WriteFile(fname, buf.Bytes(), 0666)
	if err != nil {
		Errorf(nil, "WriteFile %s failed: %v", fname, err)
	}

	return fname
}
